﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Threading;
using System.Net.Sockets;

namespace ServerFrame
{
    public class Server
    {
        int Max_con;                     //最大连接数
        int port;                        //端口
        Semaphore Max_accept;            //信号量
        Socket ListenSocket;             //监听
        int waitCon;                     //最大挂起数
        SocketAsyncEventArgs m_readWritePool; //异步操作池

        public AbsMessageCenter center;

        //MyToken tokenPool;
        ObjPool<MyToken> tokenPool;


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="con">最大连接数</param>
        /// <param name="p">端口</param>
        /// <param name="waCo">最大挂起数</param>
        public Server(int con,int p,int waCo) {
             Max_con= con;
             port=p;
             waCo = waitCon;
             Max_accept = new Semaphore(Max_con, Max_con);   //初始请求数,可以同时授予的信号量的最大请求数。
            Init();
        }

       void Init() {
            //初始化
            tokenPool=new ObjPool<MyToken> (Max_con);

            //初始化token(自定义)，并压入对象池
            for (int i = 0; i < Max_con; i++) {
                MyToken token = new MyToken();

                //Completed--异步操作的完成事件
                token.read_arg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                token.writeArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); ;

                token.center = center;
                token.send_de += ProcessSend;
                token.close_de += CloseClientSocket;
                tokenPool.Push(token);
            }

        }


        public void Start() {
            ///try---catch
            try
            {
                //新建套接字
                ListenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //AddressFamily.InterNetwork--IPV4;SocketType.Stream--TCP对应的流;
                ListenSocket.Bind(new IPEndPoint(IPAddress.Any, port));     //Any--内网，外网都监听
                ListenSocket.Listen(waitCon);                               //监听
                Console.WriteLine("Start Accept");
                StartAccept(null);
            }
            catch(Exception e) {
                Console.WriteLine(e);

            }
            
        }


        void StartAccept(SocketAsyncEventArgs e) {
            if (e==null) {
                //为空即为新连接，新建一个 e 对象，绑定接收异步操作，等待新的连接
                e = new SocketAsyncEventArgs();
                e.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
                //Accept_Completed--一个已完成的事件
            }
            else {
                //否则，把 e 中的AcceptSocket置空，等待一个新的连接
                e.AcceptSocket = null;
            }
            //开始异步监听
            Max_accept.WaitOne();

            try
            {
                bool result = ListenSocket.AcceptAsync(e);//如果挂起将返回ture
                                                          //判断异步操作有没有被挂起
                if (!result)
                {
                    ProcessAccept(e);
                }
            }
            catch (Exception ex)
            {

                Console.WriteLine(ex.ToString());
            }
        }


        void AcceptEventArg_Completed(object sender,SocketAsyncEventArgs e){
            ProcessAccept(e);
        }


        private void ProcessAccept(SocketAsyncEventArgs e) {
            //todo显示当前连接数

            //从对象池中取出读写 异步操作（读（接收异步操作））
            MyToken token = tokenPool.Pop();


            token.socket = e.AcceptSocket;

            //通知上层应用，有客户端连接
            center.OnClientConnected(token);
            token.Send(new TransModel(1, 2, 3, "hello"));

            //开始异步接收操作
            try
            {
                bool result = token.socket.ReceiveAsync(token.read_arg);

                if (!result)
                {
                    ProcessReceive(token.read_arg);
                }
            }
            catch (Exception ee) {
                Console.WriteLine(ee.ToString());
                CloseClientSocket(token, "数据连接失败");
            }
            //ObjPool<int> intpool = new ObjPool<int>(20);
            //ObjPool<Server> serverpool = new ObjPool<Server>(20);
            //SocketAsyncEventArgs readEventArgs=m_readWritePool.
            //循环利用e
            StartAccept(e);
        }


        void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            // determine which type of operation just completed and call the associated handler
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    ProcessSend(e);
                    break;
                default:
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }
        }


        void ProcessReceive(SocketAsyncEventArgs e) {
            //取出token
            MyToken token = (MyToken)e.UserToken;

            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                //接收的数据就在e.BytesTransferred
                byte[] data = new byte[e.BytesTransferred];
                Buffer.BlockCopy(e.Buffer, 0, data, 0, data.Length);

                //调用token中的接收方法，开始读取处理数据
                token.Recevie(data);
                token.socket.ReceiveAsync(e);

                try
                {
                    //循环异步接收操作
                    bool result = token.socket.ReceiveAsync(token.read_arg);
                    if (!result)
                    {  //如果挂起，继续执行它的完成事件
                        Console.WriteLine("客户端断开连接");
                        ProcessSend(token.read_arg);

                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    CloseClientSocket(token, "连接失败");
                    
                }
            }
            else {
                //Console.WriteLine(ex);

                CloseClientSocket(token, "客户端断开连接");

            }
            //读取socket,接收到的字节数存起来
                        
        }


        void ProcessSend(SocketAsyncEventArgs e) {
            //取出token
            MyToken token = (MyToken)e.UserToken;

            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                token.Write();
            }
            else
            {
                CloseClientSocket(token, "客户端断开连接");
            }
        }

        void CloseClientSocket(MyToken token, string error) {

            //通知上层，客户端断开连接，清空token数据，放入对象池中
            //MyToken token= (MyToken)e.UserToken;

            //由于上层应用可能会用到token ,所以用锁锁定token
            //此时外部不应该继续调用
            lock (token)
            {
                center.OnClientClose(token, error);
                token.Close();
                tokenPool.Push(token);
                Max_accept.Release();
            }
        }
    }
}
